﻿
//=======================================================================
// <copyright file="Twitter.cs" company="Samuel Chen Studio">
//     Copyright (c) Samuel Chen Studio. All rights reserved.
//     author   : Samuel Chen
//     version  : 1.0
//     contact  : http://www.SamuelChen.net, samuel.net@gmail.com
// </copyright>
//=======================================================================

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Web;
using System.Xml;
using System.Collections;
using System.Collections.Generic;

using System.Reflection;

namespace Net.SamuelChen.SharpTweet {

    public class Prefix {
        public const string USER = "@";
        public const string TAG = "#";
        public const string RT = "RT ";
    }

    /// <summary>
    /// The response format you prefer.
    /// </summary>
    public enum APIFormat {
        JSON,
        XML,
        RSS,
        ATOM,
    }

    public class Twitter {
      
        private string username;
        private string password;
        private bool usesProxy = false;
        private string proxy = "127.0.0.1:8000";
        private int timeout = Utility.DEFAULT_TIMEOUT;
        //private APIs.APIFormat fmt = APIs.APIFormat.XML;
        private APIHelper helper = new APIHelper();

        public Twitter() {
        }

        #region Properties

        /// <summary>
        /// The registered screen name.
        /// </summary>
        public string UserName {
            set { username = value; }
        }

        /// <summary>
        /// login password.
        /// </summary>
        public string Password {
            set { password = value; }
        }

        /// <summary>
        /// Gets or sets whether you are connecting via a proxy.
        /// </summary>
        public bool UsesProxy {
            set { usesProxy = value; }
            get { return usesProxy; }
        }

        /// <summary>
        /// Gets or sets whether you want a https connection.
        /// </summary>
        public bool UsesHttps {
            set { helper.Https = value; }
            get { return helper.Https; }
        }

        /// <summary>
        /// The proxy address. Only availible if <see cref="UsesProxy"/> is true.
        /// </summary>
        public string Proxy {
            set { proxy = value; }
            get { return proxy; }
        }

        /// <summary>
        /// Gets or sets the connection timeout (ms).
        /// </summary>
        public int Timeout {
            set { timeout = value; }
            get { return timeout; }
        }

        #endregion

        public Profile Login() {
           
            string uri = helper.GenerateUri(APIs.Account.verify_credentials);
            HttpWebRequest request = this.CreateRequest(uri);

            string content = Utility.GetResponseContent(request);
            Profile p = null;
            switch (helper.Format) {
                case APIFormat.JSON:
                    p = Profile.FromJson(content);
                    break;
                case APIFormat.XML:
                default:
                    p = Profile.FromXml(content);
                    break;
            }
            return p;
        }


        public Profile GetProfile(string name) {

            string uri = helper.GenerateUri(APIs.User.show, name);
            HttpWebRequest request = this.CreateRequest(uri);

            string content = Utility.GetResponseContent(request);
            Profile p = null;
            switch (helper.Format) {
                case APIFormat.JSON:
                    p = Profile.FromJson(content);
                    break;
                case APIFormat.XML:
                default:
                    p = Profile.FromXml(content);
                    break;
            }
            return p;
        }

        public SortedList GetTweets() {

            string uri = helper.GenerateUri(APIs.Timeline.friends_timeline);
            HttpWebRequest request = this.CreateRequest(uri);
            string content = Utility.GetResponseContent(request);
            SortedList statuses = new SortedList();

            // TODO: refactor and improve performance by reduce xmldoc construction. 
            
            switch (helper.Format) {
                case APIFormat.JSON:
                    throw new NotImplementedException();
                    break;

                case APIFormat.XML:
                default:
                    
                    XmlDocument xml = new XmlDocument();
                    try {
                        xml.LoadXml(content);
                    } catch (Exception ex) {
                        Trace.TraceWarning("Invalid XML format.");
#if DEBUG
                        throw ex;
#endif
                    }
                    XmlNodeList nodes = xml.GetElementsByTagName("status");
                    for (int i = 0; i < nodes.Count; i++) {
                        Status s = Status.FromXml(nodes[i].OuterXml);
                        if (s != null)
                            statuses.Add(s.id, s);
                    }
                    break;
            }

            return statuses;
        }

        public SortedList GetTweets(string screen_name) {

            string uri = helper.GenerateUri(APIs.Timeline.friends_timeline, screen_name);
            HttpWebRequest request = this.CreateRequest(uri);
            string content = Utility.GetResponseContent(request);
            SortedList statuses = new SortedList();

            // TODO: refactor and improve performance by reduce xmldoc construction. 

            switch (helper.Format) {
                case APIFormat.JSON:
                    throw new NotImplementedException();
                    break;

                case APIFormat.XML:
                default:

                    XmlDocument xml = new XmlDocument();
                    try {
                        xml.LoadXml(content);
                    } catch (Exception ex) {
                        Trace.TraceWarning("Invalid XML format.");
#if DEBUG
                        throw ex;
#endif
                    }
                    XmlNodeList nodes = xml.GetElementsByTagName("status");
                    for (int i = 0; i < nodes.Count; i++) {
                        Status s = Status.FromXml(nodes[i].OuterXml);
                        if (s != null)
                            statuses.Add(s.id, s);
                    }
                    break;
            }

            return statuses;
        }

        public bool PostTweet(string tweet) {
            Debug.Assert(null != tweet);

            // Encode tweet
            tweet = "Testing message from Samuel's twitter client";
            string post = string.Empty;
            using (TextWriter writer = new StringWriter()) {
                writer.Write("?status={0}", HttpUtility.UrlEncode(tweet, System.Text.Encoding.UTF8));
                post = writer.ToString();
                Debug.Print("[PostTweet] Post = {0}", post);
            }


            // Post tweet
            string uri = helper.GenerateUri(APIs.Status.update);
            HttpWebRequest request = this.CreateRequest(uri, HttpRequestMethods.POST);
            using (Stream requestStream = request.GetRequestStream()) {
                using (StreamWriter writer = new StreamWriter(requestStream)) {
                    writer.Write(post);
                }
                requestStream.Close();
            }

            Debug.Print("[PostTweet] Length: {0}", request.ContentLength);
            Debug.Print("[PostTweet] Address: {0}", request.Address);

            // Get response
            string content = Utility.GetResponseContent(request);
            Trace.TraceInformation("[PostTweet] resposne is : {0}", content);
        

            return true;
        }

        #region CreateRequest

        public HttpWebRequest CreateRequest(string uri) {
            return this.CreateRequest(uri, HttpRequestMethods.GET, this.timeout, this.usesProxy, this.proxy);
        }

        public HttpWebRequest CreateRequest(string uri, string method) {
            return this.CreateRequest(uri, method, this.timeout, this.usesProxy, this.proxy);
        }

        public HttpWebRequest CreateRequest(string uri, string method, int timeout) {
            return this.CreateRequest(uri, method, timeout, this.usesProxy, this.proxy);
        }

        /// <summary>
        /// Create a http request by given parameters
        /// </summary>
        /// <param name="uri">The uri string to request.</param>
        /// <param name="method">The reqest method such as GET, PUT, POST or DELETE.</param>
        /// <param name="timeout">The timeout of the request (ms).</param>
        /// <param name="useProxy">Whether this request will connect via proxy.</param>
        /// <param name="proxy">The proxy address string.</param>
        /// <returns>A HttpWebRequest object.</returns>
        public HttpWebRequest CreateRequest(string uri, string method, int timeout, bool useProxy, string proxy) {
            return Utility.CreateRequest(username, password, uri, method, timeout, useProxy, proxy);
        }


        #endregion


        #region Public Wrappers
        public string GenerateUserUri(string screenName) {
            return helper.BaseUrl + "/" + screenName + "/";
        }

        public string GenerateStatusUri(string screenName, string statusId) {
            return GenerateUserUri(screenName) + "statuses/" + statusId;
        }
        #endregion
    }
}